const { ccclass, property, executeInEditMode } = cc._decorator;

@ccclass("AssetItem")
class AssetItem {
    @property({
        type: cc.Asset,
        serializable: false,
    })
    _asset: cc.Asset = null

    @property({ type: cc.Asset })
    get asset(): cc.Asset {
        return this._asset
    }
    set asset(asset_: cc.Asset) {
        this._asset = asset_
        this._assetUuid = asset_['_uuid']
        this.add = this.active = this._isPrefab = asset_ instanceof cc.Prefab

        CC_EDITOR && this._parent && this._parent.refreshItems()
    }

    @property({
        visible: function (this: AssetItem) {
            return this._isPrefab
        }
    })
    get add(): boolean {
        return this._add && this._isPrefab
    }
    set add(add_: boolean) {
        this._add = add_
    }

    @property({
        visible: function (this: AssetItem) {
            return this.add
        }
    })
    get active(): boolean {
        return this._active && this._isPrefab
    }
    set active(active_: boolean) {
        this._active = active_
    }

    @property()
    _assetUuid: string = ""

    @property()
    _isPrefab: boolean = false

    @property()
    _add: boolean = false

    @property()
    _active: boolean = false

    _parent: DynamicAsset
}

@ccclass
@executeInEditMode
export default class DynamicAsset extends cc.Component {
    @property({
        type: [AssetItem]
    })
    items: AssetItem[] = []

    start() {
        cc.director.once(cc.Director.EVENT_BEFORE_UPDATE, CC_EDITOR ? this.showEditor : this.show, this)
    }

    showEditor() {
        AssetItem.prototype._parent = this
        this.refreshItems()

        for (let i = 0; i < this.items.length; i++) {
            const item = this.items[i]
            if (!item._assetUuid) {
                continue
            }

            const completeCallback = (err: Error, asset: cc.Asset) => {
                if (err) {
                    return cc.error(err)
                }
                item.asset = asset
            }

            if (cc['assetManager']) {
                cc['assetManager'].loadAny(item._assetUuid, completeCallback)
            } else {
                cc['loader'].load({ type: 'uuid', uuid: item._assetUuid }, completeCallback)
            }
        }
    }

    show() {
        const preloadUuids: string[] = []
        for (let i = 0; i < this.items.length; i++) {
            const item = this.items[i]
            if (!item._assetUuid) {
                continue
            }

            if (!item.add) {
                preloadUuids.push(item._assetUuid)
                continue
            }

            const completeCallback = (err: Error, asset: cc.Asset) => {
                if (err) {
                    return cc.error(err)
                }

                if (!this.node.isValid) {
                    return
                }

                if (asset instanceof cc.Prefab) {
                    const node = cc.instantiate(asset)
                    node.active = item.active
                    node.zIndex = i
                    node.parent = this.node
                }
            }

            if (cc['assetManager']) {
                cc['assetManager'].loadAny(item._assetUuid, completeCallback)
            } else {
                cc['loader'].load({ type: 'uuid', uuid: item._assetUuid }, completeCallback)
            }
        }

        if (preloadUuids.length > 0) {
            if (cc['assetManager']) {
                cc['assetManager'].preloadAny(preloadUuids)
            } else {
                preloadUuids.forEach(uuid => cc['loader'].load({ type: 'uuid', uuid: uuid }))
            }
        }
    }

    refreshItems() {
        if (this.items.length == 0 || this.items[this.items.length - 1]._assetUuid) {
            this.items.push(new AssetItem())
        }
    }
}
